Project

General

Profile

« Previous | Next » 

Revision 1725b607

Added by Leszek Koltunski over 1 year ago

Introduce ImplementedTablebasesList

View differences:

src/main/java/org/distorted/objectlib/tablebases/ImplementedTablebasesList.java
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.objectlib.main.ObjectType;
15

  
16
import java.lang.reflect.Constructor;
17
import java.lang.reflect.InvocationTargetException;
18

  
19
///////////////////////////////////////////////////////////////////////////////////////////////////
20

  
21
public enum ImplementedTablebasesList
22
{
23
  PYRAMINX_DUO  (ObjectType.PDUO_2, TablebasesPyraminxDuo.class),
24
  ;
25

  
26
  public static final int NUM_OBJECTS = values().length;
27

  
28
  private final ObjectType mType;
29
  private final Class<? extends TablebasesAbstract> mClass;
30

  
31
  private static final ImplementedTablebasesList[] objects;
32

  
33
  static
34
    {
35
    objects = new ImplementedTablebasesList[NUM_OBJECTS];
36
    int i=0;
37

  
38
    for(ImplementedTablebasesList object: ImplementedTablebasesList.values())
39
      {
40
      objects[i++] = object;
41
      }
42
    }
43

  
44
///////////////////////////////////////////////////////////////////////////////////////////////////
45

  
46
  public static boolean hasTablebase(ObjectType type)
47
    {
48
    for(int i=0; i<NUM_OBJECTS; i++)
49
      if( objects[i].mType == type ) return true;
50

  
51
    return false;
52
    }
53

  
54
///////////////////////////////////////////////////////////////////////////////////////////////////
55

  
56
  ImplementedTablebasesList(ObjectType type, final Class<? extends TablebasesAbstract> clazz)
57
    {
58
    mType = type;
59
    mClass= clazz;
60
    }
61

  
62
///////////////////////////////////////////////////////////////////////////////////////////////////
63

  
64
  public ObjectType getType()
65
    {
66
    return mType;
67
    }
68

  
69
///////////////////////////////////////////////////////////////////////////////////////////////////
70

  
71
  public static TablebasesAbstract createPacked(Resources res, ObjectType type)
72
    {
73
    Class<? extends TablebasesAbstract> clazz=null;
74

  
75
    for(int i=0; i<NUM_OBJECTS; i++)
76
      if( objects[i].mType == type )
77
        {
78
        clazz = objects[i].mClass;
79
        break;
80
        }
81

  
82
    if( clazz==null ) return null;
83

  
84
    try
85
      {
86
      Constructor<?>[] cons = clazz.getConstructors();
87

  
88
      if( cons.length==2 )
89
        {
90
        Object[] parameters = new Object[] { res };
91
        return (TablebasesAbstract)cons[1].newInstance(parameters);
92
        }
93
      else
94
        {
95
        android.util.Log.e("TablebasesList", "ERROR! number of TablebasesAbstract constructors="+cons.length);
96
        }
97
      }
98
    catch(IllegalAccessException iae)
99
      {
100
      android.util.Log.e("TablebasesList", "Illegal Access Exception: "+iae.getMessage());
101
      }
102
    catch(InstantiationException ie)
103
      {
104
      android.util.Log.e("TablebasesList", "Instantiation Exception: "+ie.getMessage());
105
      }
106
    catch(InvocationTargetException ite)
107
      {
108
      android.util.Log.e("TablebasesList", "Invocation Target Exception: "+ite.getMessage());
109
      }
110

  
111
    return null;
112
    }
113

  
114
///////////////////////////////////////////////////////////////////////////////////////////////////
115

  
116
  public static TablebasesAbstract createUnpacked(ObjectType type)
117
    {
118
    Class<? extends TablebasesAbstract> clazz=null;
119

  
120
    for(int i=0; i<NUM_OBJECTS; i++)
121
      if( objects[i].mType == type )
122
        {
123
        clazz = objects[i].mClass;
124
        break;
125
        }
126

  
127
    if( clazz==null ) return null;
128

  
129
    try
130
      {
131
      Constructor<?>[] cons = clazz.getConstructors();
132

  
133
      if( cons.length==2 )
134
        {
135
        return (TablebasesAbstract)cons[0].newInstance();
136
        }
137
      else
138
        {
139
        android.util.Log.e("TablebasesList", "ERROR! number of TablebasesAbstract constructors="+cons.length);
140
        }
141
      }
142
    catch(IllegalAccessException iae)
143
      {
144
      android.util.Log.e("TablebasesList", "Illegal Access Exception: "+iae.getMessage());
145
      }
146
    catch(InstantiationException ie)
147
      {
148
      android.util.Log.e("TablebasesList", "Instantiation Exception: "+ie.getMessage());
149
      }
150
    catch(InvocationTargetException ite)
151
      {
152
      android.util.Log.e("TablebasesList", "Invocation Target Exception: "+ite.getMessage());
153
      }
154

  
155
    return null;
156
    }
157
}
src/main/java/org/distorted/objectlib/tablebases/Tablebase.java
114 114
    for(int i=0; i<size; i++)
115 115
      {
116 116
      byte unpacked = retrieveUnpacked(i);
117

  
118
      if( unpacked==UNINITIALIZED )
119
        {
120
        android.util.Log.e("D", "ERROR packing: index "+i+" still uninitialized!!");
121
        }
122

  
117 123
      insertPacked(i,unpacked);
118 124
      }
119 125
/*
src/main/java/org/distorted/objectlib/tablebases/TablebasesAbstract.java
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 = true;
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
    }
303

  
304
///////////////////////////////////////////////////////////////////////////////////////////////////
305

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

  
311
///////////////////////////////////////////////////////////////////////////////////////////////////
312

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

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

  
323
///////////////////////////////////////////////////////////////////////////////////////////////////
324

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

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

  
336
///////////////////////////////////////////////////////////////////////////////////////////////////
337

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

  
346
///////////////////////////////////////////////////////////////////////////////////////////////////
347

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

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

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

  
368
///////////////////////////////////////////////////////////////////////////////////////////////////
369

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

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

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

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

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

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

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

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

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

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

  
425
        getNextAxisLayerAngle(data);
426
        }
427

  
428
      quats = getQuats(index);
429

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

  
437
    return convertMoves(moves);
438
    }
439

  
440
///////////////////////////////////////////////////////////////////////////////////////////////////
441

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

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

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

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

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

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

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

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

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

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

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

  
500
        getNextAxisLayerAngle(data);
501
        }
502

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

  
510
    return moves;
511
    }
512
}
src/main/java/org/distorted/objectlib/tablebases/TablebasesCreator.java
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 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 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
// 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
  private void getNextAxisLayerAngle(int[] data)
348
    {
349
    int axis = data[0];
350
    int layer= data[1];
351
    int angle= data[2];
352

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

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

  
367
///////////////////////////////////////////////////////////////////////////////////////////////////
368

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

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

  
380
    while(index!=0)
381
      {
382
      boolean found = false;
383

  
384
      data[0]=0;
385
      data[1]=0;
386
      data[2]=1;
387

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

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

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

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

  
412
          int childIndex = getIndex(tmpQuats);
413
          byte newLevel = mTablebase.retrievePacked(childIndex);
414

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

  
424
        getNextAxisLayerAngle(data);
425
        }
426

  
427
      quats = getQuats(index);
428

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

  
436
    return convertMoves(moves);
437
    }
438

  
439
///////////////////////////////////////////////////////////////////////////////////////////////////
440

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

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

  
452
    while(level<depth)
453
      {
454
      boolean found = false;
455

  
456
      data[0]=0;
457
      data[1]=0;
458
      data[2]=1;
459

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

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

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

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

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

  
487
          int childIndex = getIndex(tmpQuats);
488
          byte newLevel = mTablebase.retrievePacked(childIndex);
489

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

  
499
        getNextAxisLayerAngle(data);
500
        }
501

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

  
509
    return moves;
510
    }
511
}
src/main/java/org/distorted/objectlib/tablebases/TablebasesPyraminxDuo.java
18 18

  
19 19
///////////////////////////////////////////////////////////////////////////////////////////////////
20 20

  
21
public class TablebasesPyraminxDuo extends TablebasesCreator
21
public class TablebasesPyraminxDuo extends TablebasesAbstract
22 22
{
23 23
///////////////////////////////////////////////////////////////////////////////////////////////////
24 24

  
......
29 29

  
30 30
///////////////////////////////////////////////////////////////////////////////////////////////////
31 31

  
32
  public TablebasesPyraminxDuo(Resources res, int resource)
32
  public TablebasesPyraminxDuo(Resources res)
33 33
    {
34
    super(res,resource);
34
    super(res,org.distorted.objectlib.R.raw.pduo_2_tablebase);
35 35
    }
36 36

  
37 37
///////////////////////////////////////////////////////////////////////////////////////////////////

Also available in: Unified diff