Project

General

Profile

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

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

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
    mInitialized = true;
102
    InputStream stream = res.openRawResource(resource);
103
    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
104

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

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

    
125
///////////////////////////////////////////////////////////////////////////////////////////////////
126

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

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

    
145
    return ret;
146
    }
147

    
148
///////////////////////////////////////////////////////////////////////////////////////////////////
149

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

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

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

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

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

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

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

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

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

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

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

    
200
    return -1;
201
    }
202

    
203
///////////////////////////////////////////////////////////////////////////////////////////////////
204

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

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

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

    
221
    return -2;
222
    }
223

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

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

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

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

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

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

    
260
if( index==0 && ax==2 && layer==1 && angle==2 )
261
  {
262
  android.util.Log.e("D","cubit "+cubit+" belongs, oldQ="+currQuat+" quat="+quat+" new: "+newQuat);
263
  }
264
              }
265

    
266
if( tmpQuats[4]==0 &&  tmpQuats[5]==1 && tmpQuats[6]==4 && tmpQuats[7]==3 && tmpQuats[8]==10 && tmpQuats[9]==9 )
267
  {
268
  android.util.Log.e("D","index="+index+" level="+level+" ax="+ax+" layer="+layer+" angle="+angle);
269
  android.util.Log.e("D", quats[4]+" "+quats[5]+" "+quats[6]+" "+quats[7]+" "+quats[8]+" "+quats[9]);
270
  }
271

    
272
          int childIndex = getIndex(tmpQuats);
273
          if( mTablebase.insertUnpacked(childIndex,newLevel) ) ret++;
274
          }
275
        }
276

    
277
      quatBasis += (mAngles[ax][0]-1);
278
      }
279

    
280
    return ret;
281
    }
282

    
283
///////////////////////////////////////////////////////////////////////////////////////////////////
284

    
285
  public void createTablebase()
286
    {
287
    mTablebase = new Tablebase(mSize);
288
    mTablebase.insertUnpacked(0,(byte)0);
289

    
290
    int numInserted, totalInserted=1;
291
    byte insertingLevel = 0;
292

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

    
295
    do
296
      {
297
      numInserted = 0;
298

    
299
      for(int i=0; i<mSize; i++)
300
        {
301
        byte level = mTablebase.retrieveUnpacked(i);
302
        if( level==insertingLevel ) numInserted += insertAllChildren(i,level);
303
        }
304

    
305
      insertingLevel++;
306
      totalInserted += numInserted;
307
      android.util.Log.e("D", "inserted "+numInserted+" positions at level "+insertingLevel);
308
      }
309
    while( numInserted>0 );
310

    
311
    android.util.Log.e("D", "total Inserted: "+totalInserted);
312
    android.util.Log.e("D", "packing...");
313
    mTablebase.pack();
314
    android.util.Log.e("D", "all done");
315
    mInitialized = true;
316
    }
317

    
318
///////////////////////////////////////////////////////////////////////////////////////////////////
319

    
320
  public byte[] getPacked()
321
    {
322
    return mTablebase.getPacked();
323
    }
324

    
325
///////////////////////////////////////////////////////////////////////////////////////////////////
326

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

    
333
    int[] move = new int[] { axis, (1<<layer), angle };
334
    moves.add(move);
335
    }
336

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

    
339
  private void addMove(int[] moves, int axis, int layer, int angle)
340
    {
341
    int maxAngle = mAngles[axis][layer];
342
    angle = maxAngle-angle;
343
    if( angle> 0.5f*maxAngle ) angle -= maxAngle;
344

    
345
    moves[0] = axis;
346
    moves[1] = (1<<layer);
347
    moves[2] = angle;
348
    }
349

    
350
///////////////////////////////////////////////////////////////////////////////////////////////////
351

    
352
  private int[][] convertMoves(ArrayList<int[]> moves)
353
    {
354
    int len = moves.size();
355
    int[][] ret = new int[len][];
356
    for(int i=0; i<len; i++) ret[i] = moves.get(i);
357
    return ret;
358
    }
359

    
360
///////////////////////////////////////////////////////////////////////////////////////////////////
361

    
362
  private void getNextAxisLayerAngleQuat(int[] data)
363
    {
364
    int axis = data[0];
365
    int layer= data[1];
366
    int angle= data[2];
367

    
368
    if( angle< mAngles[axis][layer]-1 ) data[2]++;
369
    else
370
      {
371
      data[2] = 1;
372

    
373
      if( layer< mNumLayers[axis]-1 ) data[1]++;
374
      else
375
        {
376
        data[1] = 0;
377
        data[0] = (axis<mNumAxis-1) ? axis+1 : 0;
378
        }
379
      }
380

    
381
    data[3] = data[2];
382
    for(int i=0; i<data[0]; i++) data[3] += (mAngles[i][0]-1);
383
    }
384

    
385
///////////////////////////////////////////////////////////////////////////////////////////////////
386

    
387
  public int[][] solution(int index)
388
    {
389
    if( !mInitialized ) return null;
390

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

    
398
    while(index!=0)
399
      {
400
      boolean found = false;
401

    
402
      data[0]=0;
403
      data[1]=0;
404
      data[2]=1;
405
      data[3]=1;
406

    
407
      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
      for(int s=0; s<mScalingFactor && !found; s++)
412
        {
413
        int ax    = data[0];
414
        int layer = data[1];
415
        int angle = data[2];
416
        int quat  = data[3];
417

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

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

    
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
            }
441
          }
442

    
443
        getNextAxisLayerAngleQuat(data);
444
        }
445

    
446
      quats = getQuats(index);
447

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

    
455
    return convertMoves(moves);
456
    }
457

    
458
///////////////////////////////////////////////////////////////////////////////////////////////////
459

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

    
464
    int[] data = new int[4];
465
    int level=0;
466
    int[][] moves = new int[depth][3];
467
    int[] quats = getQuats(0);
468
    int numQuats = quats.length;
469
    int[] tmpQuats = new int[numQuats];
470

    
471
    while(level<depth)
472
      {
473
      boolean found = false;
474

    
475
      data[0]=0;
476
      data[1]=0;
477
      data[2]=1;
478
      data[3]=1;
479

    
480
      int random = rnd.nextInt(mScalingFactor);
481
      for(int i=0; i<random; i++) getNextAxisLayerAngleQuat(data);
482

    
483
      for(int ax=0; ax<mNumAxis; ax++)
484
        for(int cubit=0; cubit<mNumCubits; cubit++)
485
          mRotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
486

    
487
      for(int s=0; s<mScalingFactor && !found; s++)
488
        {
489
        int ax    = data[0];
490
        int layer = data[1];
491
        int angle = data[2];
492
        int quat  = data[3];
493

    
494
        if( mRotatable[ax][layer] )
495
          {
496
          int bitLayer = (1<<layer);
497
          System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
498

    
499
          for(int cubit=0; cubit<mNumCubits; cubit++)
500
            if( mRotRow[cubit][ax]==bitLayer )
501
              {
502
              int currQuat = tmpQuats[cubit];
503
              int newQuat = getMultQuat(quat,currQuat);
504
              tmpQuats[cubit] = newQuat;
505
              }
506

    
507
          int childIndex = getIndex(tmpQuats);
508
          byte newLevel = mTablebase.retrievePacked(childIndex);
509

    
510
          if( ((newLevel-level-1)%3) == 0 )
511
            {
512
            addMove(moves[level],ax,layer,angle);
513
            level++;
514
            quats = getQuats(childIndex);
515
            found = true;
516
            }
517
          }
518

    
519
        getNextAxisLayerAngleQuat(data);
520
        }
521

    
522
      if( !found )
523
        {
524
        android.util.Log.e("D", "scramble error: no move found!");
525
        return null;
526
        }
527
      }
528

    
529
    return moves;
530
    }
531
}
(4-4/8)