Project

General

Profile

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

distorted-objectlib / src / main / java / org / distorted / objectlib / tablebases / TablebasesCreator.java @ 352b3356

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

    
24
///////////////////////////////////////////////////////////////////////////////////////////////////
25

    
26
public abstract class TablebasesCreator
27
{
28
  private Tablebase mTablebase;
29
  private final Static3D[] mAxis;
30
  private final int mSize;
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

    
43
  private int[][] mQuatMult;
44
  private boolean mInitialized;
45

    
46
  private static final float[] mTmp = new float[4];
47

    
48
///////////////////////////////////////////////////////////////////////////////////////////////////
49

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

    
55
  abstract boolean[][] getRotatable();
56

    
57
  abstract int getSize();
58
  abstract int[] getQuats(int index);
59
  abstract int getIndex(int[] quats);
60

    
61
///////////////////////////////////////////////////////////////////////////////////////////////////
62

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

    
79
    for(int i=0; i<mNumAxis; i++)
80
      {
81
      mNumCuts[i] = (mCuts==null || mCuts[i]==null ? 0 : mCuts[i].length);
82
      }
83

    
84
    mRotRow = new int[mNumCubits][mNumAxis];
85
    mInitialized = true;
86
    }
87

    
88
///////////////////////////////////////////////////////////////////////////////////////////////////
89

    
90
  public TablebasesCreator(Resources res, int resource)
91
    {
92
    this();
93

    
94
    InputStream stream = res.openRawResource(resource);
95
    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
96

    
97
    int nRead;
98
    byte[] tmp = new byte[16384];
99

    
100
    try
101
      {
102
      while ((nRead = stream.read(tmp, 0, tmp.length)) != -1)
103
        {
104
        buffer.write(tmp, 0, nRead);
105
        }
106

    
107
      byte[] data = buffer.toByteArray();
108
      mTablebase = new Tablebase(data);
109
      }
110
    catch(IOException ex)
111
      {
112
      mInitialized = false;
113
      }
114
    }
115

    
116
///////////////////////////////////////////////////////////////////////////////////////////////////
117

    
118
  private int computeRow(float[] pos, int quat, int axisIndex)
119
    {
120
    int ret=0;
121
    int len = pos.length/3;
122
    Static3D axis = mAxis[axisIndex];
123
    float axisX = axis.get0();
124
    float axisY = axis.get1();
125
    float axisZ = axis.get2();
126
    float casted, xoff=0, yoff=0, zoff=0;
127
    Static4D q = mQuats[quat];
128

    
129
    for(int i=0; i<len; i++)
130
      {
131
      QuatHelper.rotateVectorByQuat(mTmp,pos[3*i],pos[3*i+1],pos[3*i+2],1.0f,q);
132
      casted = (mTmp[0]+xoff)*axisX + (mTmp[1]+yoff)*axisY + (mTmp[2]+zoff)*axisZ;
133
      ret |= computeSingleRow(axisIndex,casted);
134
      }
135

    
136
    return ret;
137
    }
138

    
139
///////////////////////////////////////////////////////////////////////////////////////////////////
140

    
141
  private int computeSingleRow(int axisIndex,float casted)
142
    {
143
    int num = mNumCuts[axisIndex];
144

    
145
    for(int i=0; i<num; i++)
146
      {
147
      if( casted<mCuts[axisIndex][i] ) return (1<<i);
148
      }
149

    
150
    return (1<<num);
151
    }
152

    
153
///////////////////////////////////////////////////////////////////////////////////////////////////
154
// remember about the double cover or unit quaternions!
155

    
156
  private int mulQuat(int q1, int q2)
157
    {
158
    Static4D result = QuatHelper.quatMultiply(mQuats[q1],mQuats[q2]);
159

    
160
    float rX = result.get0();
161
    float rY = result.get1();
162
    float rZ = result.get2();
163
    float rW = result.get3();
164

    
165
    final float MAX_ERROR = 0.1f;
166
    float dX,dY,dZ,dW;
167

    
168
    for(int i=0; i<mNumQuats; i++)
169
      {
170
      dX = mQuats[i].get0() - rX;
171
      dY = mQuats[i].get1() - rY;
172
      dZ = mQuats[i].get2() - rZ;
173
      dW = mQuats[i].get3() - rW;
174

    
175
      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
176
          dY<MAX_ERROR && dY>-MAX_ERROR &&
177
          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
178
          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
179

    
180
      dX = mQuats[i].get0() + rX;
181
      dY = mQuats[i].get1() + rY;
182
      dZ = mQuats[i].get2() + rZ;
183
      dW = mQuats[i].get3() + rW;
184

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

    
191
    return -1;
192
    }
193

    
194
///////////////////////////////////////////////////////////////////////////////////////////////////
195

    
196
  private int getMultQuat(int index1, int index2)
197
    {
198
    if( mQuatMult==null )
199
      {
200
      mQuatMult = new int[mNumQuats][mNumQuats];
201

    
202
      for(int i=0; i<mNumQuats; i++)
203
        for(int j=0; j<mNumQuats; j++) mQuatMult[i][j] = -1;
204
      }
205

    
206
    if( index1<mNumQuats && index2<mNumQuats )
207
      {
208
      if( mQuatMult[index1][index2]==-1 ) mQuatMult[index1][index2] = mulQuat(index1,index2);
209
      return mQuatMult[index1][index2];
210
      }
211

    
212
    return -2;
213
    }
214

    
215
///////////////////////////////////////////////////////////////////////////////////////////////////
216

    
217
  private boolean belongsToMove(int cubit, int axis, int layer)
218
    {
219
    return mRotRow[cubit][axis] == layer;
220
    }
221

    
222
///////////////////////////////////////////////////////////////////////////////////////////////////
223
// assumption: all layers have the same basicAngles!
224

    
225
  private int insertAllChildren(int index, byte level)
226
    {
227
    int ret = 0;
228
    int[] quats = getQuats(index);
229
    int numQuats = quats.length;
230
    int[] tmpQuats = new int[numQuats];
231
    byte newLevel = (byte)(level+1);
232
    boolean[] belongs = new boolean[mNumCubits];
233
    int quatBasis = 0;
234

    
235
    for(int ax=0; ax<mNumAxis; ax++)
236
      {
237
      for(int layer=0; layer<mNumLayers[ax]; layer++)
238
        {
239
        if( !mRotatable[ax][layer] ) continue;
240
        int bitLayer = (1<<layer);
241

    
242
        for(int cubit=0; cubit<mNumCubits; cubit++)
243
          {
244
          mRotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
245
          belongs[cubit] = belongsToMove(cubit,ax,bitLayer);
246
          }
247

    
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( belongs[cubit] )
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()
277
    {
278
    mTablebase = new Tablebase(mSize);
279
    mTablebase.insertUnpacked(0,(byte)0);
280

    
281
    int numInserted;
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

    
298
      android.util.Log.e("D", "inserted "+numInserted+" positions at level "+insertingLevel);
299
      }
300
    while( numInserted>0 );
301

    
302
    android.util.Log.e("D", "packing...");
303
    mTablebase.pack();
304
    android.util.Log.e("D", "all done");
305
    }
306

    
307
///////////////////////////////////////////////////////////////////////////////////////////////////
308

    
309
  public byte[] getPacked()
310
    {
311
    return mTablebase.getPacked();
312
    }
313

    
314
///////////////////////////////////////////////////////////////////////////////////////////////////
315

    
316
  private void addMove(ArrayList<int[]> moves, int axis, int layer, int angle)
317
    {
318
    int maxAngle = mAngles[axis][layer];
319
    angle = maxAngle-angle;
320
    if( angle> 0.5f*maxAngle ) angle -= maxAngle;
321

    
322
    int[] move = new int[] { axis, (1<<layer), angle };
323
    moves.add(move);
324
    }
325

    
326
///////////////////////////////////////////////////////////////////////////////////////////////////
327

    
328
  private void addMove(int[] moves, int axis, int layer, int angle)
329
    {
330
    int maxAngle = mAngles[axis][layer];
331
    angle = maxAngle-angle;
332
    if( angle> 0.5f*maxAngle ) angle -= maxAngle;
333

    
334
    moves[0] = axis;
335
    moves[1] = (1<<layer);
336
    moves[2] = angle;
337
    }
338

    
339
///////////////////////////////////////////////////////////////////////////////////////////////////
340

    
341
  private int[][] convertMoves(ArrayList<int[]> moves)
342
    {
343
    int len = moves.size();
344
    int[][] ret = new int[len][];
345
    for(int i=0; i<len; i++) ret[i] = moves.get(i);
346
    return ret;
347
    }
348

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

    
351
  public int[][] solution(int index)
352
    {
353
    if( !mInitialized ) return null;
354

    
355
    byte level = mTablebase.retrievePacked(index);
356
    ArrayList<int[]> moves = new ArrayList<>();
357
    int quatBasis = 0;
358
    int[] quats = getQuats(index);
359
    int numQuats = quats.length;
360
    int[] tmpQuats = new int[numQuats];
361
    boolean[] belongs = new boolean[mNumCubits];
362

    
363
    while(index!=0)
364
      {
365
      boolean found = false;
366

    
367
      for(int ax=0; ax<mNumAxis; ax++)
368
        {
369
        int numLayers = mNumLayers[ax];
370
        int[] angles = mAngles[ax];
371

    
372
        for(int layer=0; layer<numLayers; layer++)
373
          {
374
          if( !mRotatable[ax][layer] ) continue;
375
          int bitLayer = (1<<layer);
376

    
377
          for(int cubit=0; cubit<mNumCubits; cubit++)
378
            {
379
            mRotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
380
            belongs[cubit] = belongsToMove(cubit,ax,bitLayer);
381
            }
382

    
383
          int maxAngle = angles[layer];
384

    
385
          for(int angle=1; angle<maxAngle; angle++ )
386
            {
387
            System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
388
            int quat = quatBasis + angle;
389

    
390
            for(int cubit=0; cubit<mNumCubits; cubit++)
391
              if( belongs[cubit] )
392
                {
393
                int currQuat = tmpQuats[cubit];
394
                int newQuat = getMultQuat(quat,currQuat);
395
                tmpQuats[cubit] = newQuat;
396
                }
397

    
398
            int childIndex = getIndex(tmpQuats);
399
            byte newLevel = mTablebase.retrievePacked(childIndex);
400

    
401
            if( ((newLevel-level+1)%3) == 0 )
402
              {
403
              addMove(moves,ax,layer,angle);
404
              angle=maxAngle;
405
              layer=numLayers;
406
              ax=mNumAxis;
407
              index = childIndex;
408
              level = (level==0 ? 2 : (byte)(level-1));
409
              found = true;
410
              }
411
            }
412
          }
413

    
414
        quatBasis += (angles[0]-1);
415
        }
416

    
417
      quatBasis = 0;
418
      quats = getQuats(index);
419

    
420
      if( !found )
421
        {
422
        // error, no move found which would move us closer to a solution
423
        return null;
424
        }
425
      }
426

    
427
    return convertMoves(moves);
428
    }
429

    
430
///////////////////////////////////////////////////////////////////////////////////////////////////
431
// not working yet
432

    
433
  public int[][] scramble(int depth)
434
    {
435
    int[][] moves = new int[depth][3];
436
    int quatBasis = 0;
437
    int level=0;
438
    int[] quats = getQuats(0);
439
    int numQuats = quats.length;
440
    int[] tmpQuats = new int[numQuats];
441
    boolean[] belongs = new boolean[mNumCubits];
442

    
443
    while(level<depth)
444
      {
445
      boolean found = false;
446

    
447
      for(int ax=0; ax<mNumAxis; ax++)
448
        {
449
        int numLayers = mNumLayers[ax];
450
        int[] angles = mAngles[ax];
451

    
452
        for(int layer=0; layer<numLayers; layer++)
453
          {
454
          if( !mRotatable[ax][layer] ) continue;
455
          int bitLayer = (1<<layer);
456

    
457
          for(int cubit=0; cubit<mNumCubits; cubit++)
458
            {
459
            mRotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
460
            belongs[cubit] = belongsToMove(cubit,ax,bitLayer);
461
            }
462

    
463
          int maxAngle = angles[layer];
464

    
465
          for(int angle=1; angle<maxAngle; angle++ )
466
            {
467
            System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
468
            int quat = quatBasis + angle;
469

    
470
            for(int cubit=0; cubit<mNumCubits; cubit++)
471
              if( belongs[cubit] )
472
                {
473
                int currQuat = tmpQuats[cubit];
474
                int newQuat = getMultQuat(quat,currQuat);
475
                tmpQuats[cubit] = newQuat;
476
                }
477

    
478
            int childIndex = getIndex(tmpQuats);
479
            byte newLevel = mTablebase.retrievePacked(childIndex);
480

    
481
            if( ((newLevel-level-1)%3) == 0 )
482
              {
483
              addMove(moves[level],ax,layer,angle);
484
              angle=maxAngle;
485
              layer=numLayers;
486
              ax=mNumAxis;
487
              quatBasis = 0;
488
              quats = getQuats(childIndex);
489
              level++;
490
              found = true;
491
              }
492
            }
493
          }
494

    
495
        quatBasis += (angles[0]-1);
496
        }
497

    
498
      if( !found )
499
        {
500
        // error, no move found which would move us 1 step further
501
        return null;
502
        }
503
      }
504

    
505
    return moves;
506
    }
507
}
(2-2/3)