Project

General

Profile

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

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

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 TablebasesCreator
28
{
29
  private Tablebase mTablebase;
30
  private final Static3D[] mAxis;
31
  private final int mSize;
32
  private final int[][] mAngles;
33
  private final int mNumAxis;
34
  private final int[] mNumLayers;
35
  private final int mNumQuats;
36
  private final Static4D[] mQuats;
37
  private final int[][] mRotRow;
38
  private final int mNumCubits;
39
  private final float[][] mPosition;
40
  private final float[][] mCuts;
41
  private final int[] mNumCuts;
42
  private final boolean[][] mRotatable;
43
  private final int mScalingFactor;
44

    
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 TablebasesCreator()
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 = true;
93
    }
94

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

    
97
  public TablebasesCreator(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

    
114
      byte[] data = buffer.toByteArray();
115
      mTablebase = new Tablebase(data);
116
      }
117
    catch(IOException ex)
118
      {
119
      mInitialized = false;
120
      }
121
    }
122

    
123
///////////////////////////////////////////////////////////////////////////////////////////////////
124

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

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

    
143
    return ret;
144
    }
145

    
146
///////////////////////////////////////////////////////////////////////////////////////////////////
147

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

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

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

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

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

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

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

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

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

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

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

    
198
    return -1;
199
    }
200

    
201
///////////////////////////////////////////////////////////////////////////////////////////////////
202

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

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

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

    
219
    return -2;
220
    }
221

    
222
///////////////////////////////////////////////////////////////////////////////////////////////////
223

    
224
  private boolean belongsToMove(int cubit, int axis, int layer)
225
    {
226
    return mRotRow[cubit][axis] == layer;
227
    }
228

    
229
///////////////////////////////////////////////////////////////////////////////////////////////////
230
// assumption: all layers have the same basicAngles!
231

    
232
  private int insertAllChildren(int index, byte level)
233
    {
234
    int ret = 0;
235
    int[] quats = getQuats(index);
236
    int numQuats = quats.length;
237
    int[] tmpQuats = new int[numQuats];
238
    byte newLevel = (byte)(level+1);
239
    int quatBasis = 0;
240

    
241
    for(int ax=0; ax<mNumAxis; ax++)
242
      for(int cubit=0; cubit<mNumCubits; cubit++)
243
        mRotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
244

    
245
    for(int ax=0; ax<mNumAxis; ax++)
246
      {
247
      for(int layer=0; layer<mNumLayers[ax]; layer++)
248
        {
249
        if( !mRotatable[ax][layer] ) continue;
250
        int bitLayer = (1<<layer);
251
        int maxAngle = mAngles[ax][layer];
252

    
253
        for(int angle=1; angle<maxAngle; angle++ )
254
          {
255
          System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
256
          int quat = quatBasis + angle;
257

    
258
          for(int cubit=0; cubit<mNumCubits; cubit++)
259
            if( mRotRow[cubit][ax]==bitLayer )
260
              {
261
              int currQuat = tmpQuats[cubit];
262
              int newQuat = getMultQuat(quat,currQuat);
263
              tmpQuats[cubit] = newQuat;
264
              }
265

    
266
          int childIndex = getIndex(tmpQuats);
267
          if( mTablebase.insertUnpacked(childIndex,newLevel) ) ret++;
268
          }
269
        }
270

    
271
      quatBasis += (mAngles[ax][0]-1);
272
      }
273

    
274
    return ret;
275
    }
276

    
277
///////////////////////////////////////////////////////////////////////////////////////////////////
278

    
279
  public void createTablebase()
280
    {
281
    mTablebase = new Tablebase(mSize);
282
    mTablebase.insertUnpacked(0,(byte)0);
283

    
284
    int numInserted;
285
    byte insertingLevel = 0;
286

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

    
289
    do
290
      {
291
      numInserted = 0;
292

    
293
      for(int i=0; i<mSize; i++)
294
        {
295
        byte level = mTablebase.retrieveUnpacked(i);
296
        if( level==insertingLevel ) numInserted += insertAllChildren(i,level);
297
        }
298

    
299
      insertingLevel++;
300

    
301
      android.util.Log.e("D", "inserted "+numInserted+" positions at level "+insertingLevel);
302
      }
303
    while( numInserted>0 );
304

    
305
    android.util.Log.e("D", "packing...");
306
    mTablebase.pack();
307
    android.util.Log.e("D", "all done");
308
    }
309

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

    
312
  public byte[] getPacked()
313
    {
314
    return mTablebase.getPacked();
315
    }
316

    
317
///////////////////////////////////////////////////////////////////////////////////////////////////
318

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

    
325
    int[] move = new int[] { axis, (1<<layer), angle };
326
    moves.add(move);
327
    }
328

    
329
///////////////////////////////////////////////////////////////////////////////////////////////////
330

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

    
337
    moves[0] = axis;
338
    moves[1] = (1<<layer);
339
    moves[2] = angle;
340
    }
341

    
342
///////////////////////////////////////////////////////////////////////////////////////////////////
343

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

    
352
///////////////////////////////////////////////////////////////////////////////////////////////////
353

    
354
  private void getNextAxisLayerAngle(int[] data)
355
    {
356
    int axis = data[0];
357
    int layer= data[1];
358
    int angle= data[2];
359

    
360
    if( angle< mAngles[axis][layer]-1 ) data[2]++;
361
    else
362
      {
363
      data[2] = 1;
364

    
365
      if( layer< mNumLayers[axis]-1 ) data[1]++;
366
      else
367
        {
368
        data[1] = 0;
369
        data[0] = (axis<mNumAxis-1) ? axis+1 : 0;
370
        }
371
      }
372
    }
373

    
374
///////////////////////////////////////////////////////////////////////////////////////////////////
375

    
376
  public int[][] solution(int index)
377
    {
378
    if( !mInitialized ) return null;
379

    
380
    int[] data = new int[3];
381
    byte level = mTablebase.retrievePacked(index);
382
    ArrayList<int[]> moves = new ArrayList<>();
383
    int[] quats = getQuats(index);
384
    int numQuats = quats.length;
385
    int[] tmpQuats = new int[numQuats];
386

    
387
    while(index!=0)
388
      {
389
      boolean found = false;
390

    
391
      data[0]=0;
392
      data[1]=0;
393
      data[2]=1;
394

    
395
      for(int ax=0; ax<mNumAxis; ax++)
396
        for(int cubit=0; cubit<mNumCubits; cubit++)
397
          mRotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
398

    
399
      for(int s=0; s<mScalingFactor && !found; s++)
400
        {
401
        int ax    = data[0];
402
        int layer = data[1];
403
        int angle = data[2];
404

    
405
        if( mRotatable[ax][layer] )
406
          {
407
          int bitLayer = (1<<layer);
408
          System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
409
          int quat = ax*(mAngles[ax][0]-1) + angle;
410

    
411
          for(int cubit=0; cubit<mNumCubits; cubit++)
412
            if( mRotRow[cubit][ax]==bitLayer )
413
              {
414
              int currQuat = tmpQuats[cubit];
415
              int newQuat = getMultQuat(quat,currQuat);
416
              tmpQuats[cubit] = newQuat;
417
              }
418

    
419
          int childIndex = getIndex(tmpQuats);
420
          byte newLevel = mTablebase.retrievePacked(childIndex);
421

    
422
          if( ((newLevel-level+1)%3) == 0 )
423
            {
424
            addMove(moves,ax,layer,angle);
425
            index = childIndex;
426
            level = (level==0 ? 2 : (byte)(level-1));
427
            found = true;
428
            }
429
          }
430

    
431
        getNextAxisLayerAngle(data);
432
        }
433

    
434
      quats = getQuats(index);
435

    
436
      if( !found )
437
        {
438
        // error, no move found which would move us closer to a solution
439
        return null;
440
        }
441
      }
442

    
443
    return convertMoves(moves);
444
    }
445

    
446
///////////////////////////////////////////////////////////////////////////////////////////////////
447

    
448
  public int[][] scramble(Random rnd, int depth)
449
    {
450
    if( !mInitialized ) return null;
451

    
452
    int[] data = new int[3];
453
    int level=0;
454
    int[][] moves = new int[depth][3];
455
    int[] quats = getQuats(0);
456
    int numQuats = quats.length;
457
    int[] tmpQuats = new int[numQuats];
458

    
459
    while(level<depth)
460
      {
461
      boolean found = false;
462

    
463
      data[0]=0;
464
      data[1]=0;
465
      data[2]=1;
466

    
467
      int random = rnd.nextInt(mScalingFactor);
468
      for(int i=0; i<random; i++) getNextAxisLayerAngle(data);
469

    
470
      for(int ax=0; ax<mNumAxis; ax++)
471
        for(int cubit=0; cubit<mNumCubits; cubit++)
472
          mRotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
473

    
474
      for(int s=0; s<mScalingFactor && !found; s++)
475
        {
476
        int ax    = data[0];
477
        int layer = data[1];
478
        int angle = data[2];
479

    
480
        if( mRotatable[ax][layer] )
481
          {
482
          int bitLayer = (1<<layer);
483
          System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
484
          int quat = ax*(mAngles[ax][0]-1) + angle;
485

    
486
          for(int cubit=0; cubit<mNumCubits; cubit++)
487
            if( mRotRow[cubit][ax]==bitLayer )
488
              {
489
              int currQuat = tmpQuats[cubit];
490
              int newQuat = getMultQuat(quat,currQuat);
491
              tmpQuats[cubit] = newQuat;
492
              }
493

    
494
          int childIndex = getIndex(tmpQuats);
495
          byte newLevel = mTablebase.retrievePacked(childIndex);
496

    
497
          if( ((newLevel-level-1)%3) == 0 )
498
            {
499
            addMove(moves[level],ax,layer,angle);
500
            level++;
501
            quats = getQuats(childIndex);
502
            found = true;
503
            }
504
          }
505

    
506
        getNextAxisLayerAngle(data);
507
        }
508

    
509
      if( !found )
510
        {
511
        // error, no move found which would move us closer to a solution
512
        return null;
513
        }
514
      }
515

    
516
    return moves;
517
    }
518
}
(2-2/3)