Project

General

Profile

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

distorted-objectlib / src / main / java / org / distorted / objectlib / tablebases / TablebasesAbstract.java @ 5e501d98

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;
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 getSize();
60
  abstract int[] getQuats(int index);
61
  abstract int getIndex(int[] quats);
62

    
63
///////////////////////////////////////////////////////////////////////////////////////////////////
64

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

    
81
    int scaling = 0;
82

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

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

    
95
///////////////////////////////////////////////////////////////////////////////////////////////////
96

    
97
  public TablebasesAbstract(Resources res, int resource)
98
    {
99
    this();
100

    
101
    InputStream stream = res.openRawResource(resource);
102
    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
103

    
104
    int nRead;
105
    byte[] tmp = new byte[16384];
106

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

    
124
///////////////////////////////////////////////////////////////////////////////////////////////////
125

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

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

    
144
    return ret;
145
    }
146

    
147
///////////////////////////////////////////////////////////////////////////////////////////////////
148

    
149
  private int computeSingleRow(int axisIndex,float casted)
150
    {
151
    int num = mNumCuts[axisIndex];
152

    
153
    for(int i=0; i<num; i++)
154
      {
155
      if( casted<mCuts[axisIndex][i] ) return (1<<i);
156
      }
157

    
158
    return (1<<num);
159
    }
160

    
161
///////////////////////////////////////////////////////////////////////////////////////////////////
162
// remember about the double cover or unit quaternions!
163

    
164
  private int mulQuat(int q1, int q2)
165
    {
166
    Static4D result = QuatHelper.quatMultiply(mQuats[q1],mQuats[q2]);
167

    
168
    float rX = result.get0();
169
    float rY = result.get1();
170
    float rZ = result.get2();
171
    float rW = result.get3();
172

    
173
    final float MAX_ERROR = 0.1f;
174
    float dX,dY,dZ,dW;
175

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

    
183
      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
184
          dY<MAX_ERROR && dY>-MAX_ERROR &&
185
          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
186
          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
187

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

    
193
      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
194
          dY<MAX_ERROR && dY>-MAX_ERROR &&
195
          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
196
          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
197
      }
198

    
199
    return -1;
200
    }
201

    
202
///////////////////////////////////////////////////////////////////////////////////////////////////
203

    
204
  private int getMultQuat(int index1, int index2)
205
    {
206
    if( mQuatMult==null )
207
      {
208
      mQuatMult = new int[mNumQuats][mNumQuats];
209

    
210
      for(int i=0; i<mNumQuats; i++)
211
        for(int j=0; j<mNumQuats; j++) mQuatMult[i][j] = -1;
212
      }
213

    
214
    if( index1<mNumQuats && index2<mNumQuats )
215
      {
216
      if( mQuatMult[index1][index2]==-1 ) mQuatMult[index1][index2] = mulQuat(index1,index2);
217
      return mQuatMult[index1][index2];
218
      }
219

    
220
    return -2;
221
    }
222

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

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

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

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

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

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

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

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

    
268
    return ret;
269
    }
270

    
271
///////////////////////////////////////////////////////////////////////////////////////////////////
272

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

    
278
    int numInserted;
279
    byte insertingLevel = 0;
280

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

    
283
    do
284
      {
285
      numInserted = 0;
286

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

    
293
      insertingLevel++;
294

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

    
299
    android.util.Log.e("D", "packing...");
300
    mTablebase.pack();
301
    android.util.Log.e("D", "all done");
302
    mInitialized = true;
303
    }
304

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

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

    
312
///////////////////////////////////////////////////////////////////////////////////////////////////
313

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

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

    
324
///////////////////////////////////////////////////////////////////////////////////////////////////
325

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

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

    
337
///////////////////////////////////////////////////////////////////////////////////////////////////
338

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

    
347
///////////////////////////////////////////////////////////////////////////////////////////////////
348

    
349
  private void getNextAxisLayerAngle(int[] data)
350
    {
351
    int axis = data[0];
352
    int layer= data[1];
353
    int angle= data[2];
354

    
355
    if( angle< mAngles[axis][layer]-1 ) data[2]++;
356
    else
357
      {
358
      data[2] = 1;
359

    
360
      if( layer< mNumLayers[axis]-1 ) data[1]++;
361
      else
362
        {
363
        data[1] = 0;
364
        data[0] = (axis<mNumAxis-1) ? axis+1 : 0;
365
        }
366
      }
367
    }
368

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

    
371
  public int[][] solution(int index)
372
    {
373
    if( !mInitialized ) return null;
374

    
375
    int[] data = new int[3];
376
    byte level = mTablebase.retrievePacked(index);
377
    ArrayList<int[]> moves = new ArrayList<>();
378
    int[] quats = getQuats(index);
379
    int numQuats = quats.length;
380
    int[] tmpQuats = new int[numQuats];
381

    
382
    while(index!=0)
383
      {
384
      boolean found = false;
385

    
386
      data[0]=0;
387
      data[1]=0;
388
      data[2]=1;
389

    
390
      for(int ax=0; ax<mNumAxis; ax++)
391
        for(int cubit=0; cubit<mNumCubits; cubit++)
392
          mRotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
393

    
394
      for(int s=0; s<mScalingFactor && !found; s++)
395
        {
396
        int ax    = data[0];
397
        int layer = data[1];
398
        int angle = data[2];
399

    
400
        if( mRotatable[ax][layer] )
401
          {
402
          int bitLayer = (1<<layer);
403
          System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
404
          int quat = ax*(mAngles[ax][0]-1) + angle;
405

    
406
          for(int cubit=0; cubit<mNumCubits; cubit++)
407
            if( mRotRow[cubit][ax]==bitLayer )
408
              {
409
              int currQuat = tmpQuats[cubit];
410
              int newQuat = getMultQuat(quat,currQuat);
411
              tmpQuats[cubit] = newQuat;
412
              }
413

    
414
          int childIndex = getIndex(tmpQuats);
415
          byte newLevel = mTablebase.retrievePacked(childIndex);
416

    
417
          if( ((newLevel-level+1)%3) == 0 )
418
            {
419
            addMove(moves,ax,layer,angle);
420
            index = childIndex;
421
            level = (level==0 ? 2 : (byte)(level-1));
422
            found = true;
423
            }
424
          }
425

    
426
        getNextAxisLayerAngle(data);
427
        }
428

    
429
      quats = getQuats(index);
430

    
431
      if( !found )
432
        {
433
        // error, no move found which would move us closer to a solution
434
        return null;
435
        }
436
      }
437

    
438
    return convertMoves(moves);
439
    }
440

    
441
///////////////////////////////////////////////////////////////////////////////////////////////////
442

    
443
  public int[][] scramble(Random rnd, int depth)
444
    {
445
    if( !mInitialized ) return null;
446

    
447
    int[] data = new int[3];
448
    int level=0;
449
    int[][] moves = new int[depth][3];
450
    int[] quats = getQuats(0);
451
    int numQuats = quats.length;
452
    int[] tmpQuats = new int[numQuats];
453

    
454
    while(level<depth)
455
      {
456
      boolean found = false;
457

    
458
      data[0]=0;
459
      data[1]=0;
460
      data[2]=1;
461

    
462
      int random = rnd.nextInt(mScalingFactor);
463
      for(int i=0; i<random; i++) getNextAxisLayerAngle(data);
464

    
465
      for(int ax=0; ax<mNumAxis; ax++)
466
        for(int cubit=0; cubit<mNumCubits; cubit++)
467
          mRotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
468

    
469
      for(int s=0; s<mScalingFactor && !found; s++)
470
        {
471
        int ax    = data[0];
472
        int layer = data[1];
473
        int angle = data[2];
474

    
475
        if( mRotatable[ax][layer] )
476
          {
477
          int bitLayer = (1<<layer);
478
          System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
479
          int quat = ax*(mAngles[ax][0]-1) + angle;
480

    
481
          for(int cubit=0; cubit<mNumCubits; cubit++)
482
            if( mRotRow[cubit][ax]==bitLayer )
483
              {
484
              int currQuat = tmpQuats[cubit];
485
              int newQuat = getMultQuat(quat,currQuat);
486
              tmpQuats[cubit] = newQuat;
487
              }
488

    
489
          int childIndex = getIndex(tmpQuats);
490
          byte newLevel = mTablebase.retrievePacked(childIndex);
491

    
492
          if( ((newLevel-level-1)%3) == 0 )
493
            {
494
            addMove(moves[level],ax,layer,angle);
495
            level++;
496
            quats = getQuats(childIndex);
497
            found = true;
498
            }
499
          }
500

    
501
        getNextAxisLayerAngle(data);
502
        }
503

    
504
      if( !found )
505
        {
506
        // error, no move found which would move us closer to a solution
507
        return null;
508
        }
509
      }
510

    
511
    return moves;
512
    }
513
}
(3-3/5)