Project

General

Profile

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

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

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
    int quatBasis = 0;
233

    
234
    for(int ax=0; ax<mNumAxis; ax++)
235
      for(int cubit=0; cubit<mNumCubits; cubit++)
236
        mRotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
237

    
238
    for(int ax=0; ax<mNumAxis; ax++)
239
      {
240
      for(int layer=0; layer<mNumLayers[ax]; layer++)
241
        {
242
        if( !mRotatable[ax][layer] ) continue;
243
        int bitLayer = (1<<layer);
244
        int maxAngle = mAngles[ax][layer];
245

    
246
        for(int angle=1; angle<maxAngle; angle++ )
247
          {
248
          System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
249
          int quat = quatBasis + angle;
250

    
251
          for(int cubit=0; cubit<mNumCubits; cubit++)
252
            if( mRotRow[cubit][ax]==bitLayer )
253
              {
254
              int currQuat = tmpQuats[cubit];
255
              int newQuat = getMultQuat(quat,currQuat);
256
              tmpQuats[cubit] = newQuat;
257
              }
258

    
259
          int childIndex = getIndex(tmpQuats);
260
          if( mTablebase.insertUnpacked(childIndex,newLevel) ) ret++;
261
          }
262
        }
263

    
264
      quatBasis += (mAngles[ax][0]-1);
265
      }
266

    
267
    return ret;
268
    }
269

    
270
///////////////////////////////////////////////////////////////////////////////////////////////////
271

    
272
  public void createTablebase()
273
    {
274
    mTablebase = new Tablebase(mSize);
275
    mTablebase.insertUnpacked(0,(byte)0);
276

    
277
    int numInserted;
278
    byte insertingLevel = 0;
279

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

    
282
    do
283
      {
284
      numInserted = 0;
285

    
286
      for(int i=0; i<mSize; i++)
287
        {
288
        byte level = mTablebase.retrieveUnpacked(i);
289
        if( level==insertingLevel ) numInserted += insertAllChildren(i,level);
290
        }
291

    
292
      insertingLevel++;
293

    
294
      android.util.Log.e("D", "inserted "+numInserted+" positions at level "+insertingLevel);
295
      }
296
    while( numInserted>0 );
297

    
298
    android.util.Log.e("D", "packing...");
299
    mTablebase.pack();
300
    android.util.Log.e("D", "all done");
301
    }
302

    
303
///////////////////////////////////////////////////////////////////////////////////////////////////
304

    
305
  public byte[] getPacked()
306
    {
307
    return mTablebase.getPacked();
308
    }
309

    
310
///////////////////////////////////////////////////////////////////////////////////////////////////
311

    
312
  private void addMove(ArrayList<int[]> moves, int axis, int layer, int angle)
313
    {
314
    int maxAngle = mAngles[axis][layer];
315
    angle = maxAngle-angle;
316
    if( angle> 0.5f*maxAngle ) angle -= maxAngle;
317

    
318
    int[] move = new int[] { axis, (1<<layer), angle };
319
    moves.add(move);
320
    }
321

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

    
324
  private void addMove(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
    moves[0] = axis;
331
    moves[1] = (1<<layer);
332
    moves[2] = angle;
333
    }
334

    
335
///////////////////////////////////////////////////////////////////////////////////////////////////
336

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

    
345
///////////////////////////////////////////////////////////////////////////////////////////////////
346

    
347
  public int[][] solution(int index)
348
    {
349
    if( !mInitialized ) return null;
350

    
351
    byte level = mTablebase.retrievePacked(index);
352
    ArrayList<int[]> moves = new ArrayList<>();
353
    int quatBasis = 0;
354
    int[] quats = getQuats(index);
355
    int numQuats = quats.length;
356
    int[] tmpQuats = new int[numQuats];
357

    
358
    while(index!=0)
359
      {
360
      boolean found = false;
361

    
362
      for(int ax=0; ax<mNumAxis; ax++)
363
        for(int cubit=0; cubit<mNumCubits; cubit++)
364
          mRotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
365

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

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

    
377
          for(int angle=1; angle<maxAngle; angle++ )
378
            {
379
            System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
380
            int quat = quatBasis + angle;
381

    
382
            for(int cubit=0; cubit<mNumCubits; cubit++)
383
              if( mRotRow[cubit][ax]==bitLayer )
384
                {
385
                int currQuat = tmpQuats[cubit];
386
                int newQuat = getMultQuat(quat,currQuat);
387
                tmpQuats[cubit] = newQuat;
388
                }
389

    
390
            int childIndex = getIndex(tmpQuats);
391
            byte newLevel = mTablebase.retrievePacked(childIndex);
392

    
393
            if( ((newLevel-level+1)%3) == 0 )
394
              {
395
              addMove(moves,ax,layer,angle);
396
              angle=maxAngle;
397
              layer=numLayers;
398
              ax=mNumAxis;
399
              index = childIndex;
400
              level = (level==0 ? 2 : (byte)(level-1));
401
              found = true;
402
              }
403
            }
404
          }
405

    
406
        quatBasis += (angles[0]-1);
407
        }
408

    
409
      quatBasis = 0;
410
      quats = getQuats(index);
411

    
412
      if( !found )
413
        {
414
        // error, no move found which would move us closer to a solution
415
        return null;
416
        }
417
      }
418

    
419
    return convertMoves(moves);
420
    }
421

    
422
///////////////////////////////////////////////////////////////////////////////////////////////////
423
// not working yet
424

    
425
  public int[][] scramble(int depth)
426
    {
427
    int[][] moves = new int[depth][3];
428
    int quatBasis = 0;
429
    int level=0;
430
    int[] quats = getQuats(0);
431
    int numQuats = quats.length;
432
    int[] tmpQuats = new int[numQuats];
433

    
434
    while(level<depth)
435
      {
436
      boolean found = false;
437

    
438
      for(int ax=0; ax<mNumAxis; ax++)
439
        for(int cubit=0; cubit<mNumCubits; cubit++)
440
          mRotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
441

    
442
      for(int ax=0; ax<mNumAxis; ax++)
443
        {
444
        int numLayers = mNumLayers[ax];
445
        int[] angles = mAngles[ax];
446

    
447
        for(int layer=0; layer<numLayers; layer++)
448
          {
449
          if( !mRotatable[ax][layer] ) continue;
450
          int bitLayer = (1<<layer);
451
          int maxAngle = angles[layer];
452

    
453
          for(int angle=1; angle<maxAngle; angle++ )
454
            {
455
            System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
456
            int quat = quatBasis + angle;
457

    
458
            for(int cubit=0; cubit<mNumCubits; cubit++)
459
              if( mRotRow[cubit][ax]==bitLayer )
460
                {
461
                int currQuat = tmpQuats[cubit];
462
                int newQuat = getMultQuat(quat,currQuat);
463
                tmpQuats[cubit] = newQuat;
464
                }
465

    
466
            int childIndex = getIndex(tmpQuats);
467
            byte newLevel = mTablebase.retrievePacked(childIndex);
468

    
469
            if( ((newLevel-level-1)%3) == 0 )
470
              {
471
              addMove(moves[level],ax,layer,angle);
472
              angle=maxAngle;
473
              layer=numLayers;
474
              ax=mNumAxis;
475
              quatBasis = 0;
476
              quats = getQuats(childIndex);
477
              level++;
478
              found = true;
479
              }
480
            }
481
          }
482

    
483
        quatBasis += (angles[0]-1);
484
        }
485

    
486
      if( !found )
487
        {
488
        // error, no move found which would move us 1 step further
489
        return null;
490
        }
491
      }
492

    
493
    return moves;
494
    }
495
}
(2-2/3)